home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************************************/
- /* */
- /* Module: POKEY Chip Simulator, V1.2 */
- /* Purpose: To emulate the sound generation hardware of the Atari POKEY chip.*/
- /* Author: Ron Fries */
- /* Thomas Richter */
- /* Date: September 22, 1996 */
- /* February 17,1998 */
- /* */
- /*****************************************************************************/
- /* */
- /* License Information and Copyright Notice */
- /* ======================================== */
- /* */
- /* PokeySound is Copyright(c) 1996 by Ron Fries */
- /* */
- /* This library is free software; you can redistribute it and/or modify it */
- /* under the terms of version 2 of the GNU Library General Public License */
- /* as published by the Free Software Foundation. */
- /* */
- /* This library is distributed in the hope that it will be useful, but */
- /* WITHOUT ANY WARRANTY; without even the implied warranty of */
- /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library */
- /* General Public License for more details. */
- /* To obtain a copy of the GNU Library General Public License, write to the */
- /* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
- /* */
- /* Any permitted reproduction of these routines, in whole or in part, must */
- /* bear this legend. */
- /* */
- /*****************************************************************************/
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
-
- #include "pokey.h"
- #include "pokey11.h"
- #include "sio.h"
- #include "mem.h"
- #include "cpu.h"
- #include "platform.h"
-
- /* CONSTANT DEFINITIONS */
-
- /* definitions for AUDCx (D201, D203, D205, D207) */
- #define NOTPOLY5 0x80 /* selects POLY5 or direct CLOCK */
- #define POLY4 0x40 /* selects POLY4 or POLY17 */
- #define PURE 0x20 /* selects POLY4/17 or PURE tone */
- #define VOL_ONLY 0x10 /* selects VOLUME OUTPUT ONLY */
- #define VOLUME_MASK 0x0f /* volume mask */
-
- /* definitions for AUDCTL (D208) */
- #define POLY9 0x80 /* selects POLY9 or POLY17 */
- #define CH1_179 0x40 /* selects 1.78979 MHz for Ch 1 */
- #define CH3_179 0x20 /* selects 1.78979 MHz for Ch 3 */
- #define CH1_CH2 0x10 /* clocks channel 1 w/channel 2 */
- #define CH3_CH4 0x08 /* clocks channel 3 w/channel 4 */
- #define CH1_FILTER 0x04 /* selects channel 1 high pass filter */
- #define CH2_FILTER 0x02 /* selects channel 2 high pass filter */
- #define CLOCK_15 0x01 /* selects 15.6999kHz or 63.9210kHz */
-
-
- /* for accuracy, the 64kHz and 15kHz clocks are exact divisions of
- the 1.79MHz clock */
- #define DIV_64 28 /* divisor for 1.79MHz clock to 64 kHz */
- #define DIV_15 114 /* divisor for 1.79MHz clock to 15 kHz */
-
- /* the size (in entries) of the 4 polynomial tables */
- #define POLY4_SIZE 0x000f
- #define POLY5_SIZE 0x001f
- #define POLY9_SIZE 0x01ff
-
- #ifdef COMP16 /* if 16-bit compiler */
- #define POLY17_SIZE 0x00007fffL /* reduced to 15 bits for simplicity */
- #else
- #define POLY17_SIZE 0x0001ffffL /* else use the full 17 bits */
- #endif
-
-
- #define FALSE 0
- #define TRUE 1
-
-
- /* GLOBAL VARIABLE DEFINITIONS */
-
- /* structures to hold the 9 pokey control bytes */
- UBYTE AUDF[4]; /* AUDFx (D200, D202, D204, D206) */
- UBYTE AUDC[4]; /* AUDCx (D201, D203, D205, D207) */
- UBYTE AUDCTL; /* AUDCTL (D208) */
-
- static UBYTE Outbit[4]; /* current state of the output (high or low) */
-
- static UBYTE Outvol[4]; /* last output volume for each channel */
-
- int DELAYED_SERIN_IRQ;
- int DELAYED_SEROUT_IRQ;
- int DELAYED_XMTDONE_IRQ;
-
- /* Initialze the bit patterns for the polynomials. */
-
- /* The 4bit and 5bit patterns are the identical ones used in the pokey chip. */
- /* Though the patterns could be packed with 8 bits per byte, using only a */
- /* single bit per byte keeps the math simple, which is important for */
- /* efficient processing. */
-
- static UBYTE bit4[POLY4_SIZE] =
- { 1,1,0,1,1,1,0,0,0,0,1,0,1,0,0 };
-
- static UBYTE bit5[POLY5_SIZE] =
- { 0,0,1,1,0,0,0,1,1,1,1,0,0,1,0,1,0,1,1,0,1,1,1,0,1,0,0,0,0,0,1 };
-
- static UBYTE bit17[POLY17_SIZE]; /* Rather than have a table with 131071 */
- /* entries, I use a random number generator. */
- /* It shouldn't make much difference since */
- /* the pattern rarely repeats anyway. */
-
- static ULONG Poly17_size; /* global for the poly 17 size, since it can */
- /* be changed from 17 bit to 9 bit */
-
- static ULONG Poly_adjust; /* the amount that the polynomial will need */
- /* to be adjusted to process the next bit */
-
- static ULONG P4=0, /* Global position pointer for the 4-bit POLY array */
- P5=0, /* Global position pointer for the 5-bit POLY array */
- P17=0; /* Global position pointer for the 17-bit POLY array */
-
- ULONG Div_n_cnt[4], /* Divide by n counter. one for each channel */
- Div_n_max[4], /* Divide by n maximum, one for each channel */
- Div_n_irq[4]; /* Just the same counter, but used for IRQ */
-
- static ULONG Samp_n_max, /* Sample max. For accuracy, it is *256 */
- Samp_n_cnt[2]; /* Sample cnt. */
-
- static ULONG Base_mult; /* selects either 64Khz or 15Khz clock mult */
-
- UBYTE KBCODE;
- UBYTE IRQST;
- UBYTE IRQEN;
- UBYTE SKSTAT;
-
- static char *rcsid = "$Id: pokey11.c,v 1.2 1998/02/17 Ron Fries,thor";
-
- extern UWORD regPC;
-
- void Update_Sound(int chan_mask);
-
- /*****************************************************************************/
- /* In my routines, I treat the sample output as another divide by N counter */
- /* For better accuracy, the Samp_n_cnt has a fixed binary decimal point */
- /* which has 8 binary digits to the right of the decimal point. I use a two */
- /* byte array to give me a minimum of 40 bits, and then use pointer math to */
- /* reference either the 24.8 whole/fraction combination or the 32-bit whole */
- /* only number. This is mainly used to keep the math simple for */
- /* optimization. See below: */
- /* */
- /* xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | xxxxxxxx xxxxxxxx xxxxxxxx.xxxxxxxx */
- /* unused unused unused whole whole whole whole fraction */
- /* */
- /* Samp_n_cnt[0] gives me a 32-bit int 24 whole bits with 8 fractional bits, */
- /* while (ULONG *)((UBYTE *)(&Samp_n_cnt[0])+1) gives me the 32-bit whole */
- /* number only. */
- /*****************************************************************************/
-
- /* Register GET routines */
-
- mtype POKEY_KBCODE_GET(void)
- {
- return KBCODE;
- }
-
- mtype POKEY_IRQST_GET(void)
- {
- return IRQST;
- }
-
- mtype POKEY_POT0_GET(void)
- {
- return Atari_POT(0);
- }
-
- mtype POKEY_POT1_GET(void)
- {
- return Atari_POT(1);
- }
-
- mtype POKEY_POT2_GET(void)
- {
- return Atari_POT(2);
- }
-
- mtype POKEY_POT3_GET(void)
- {
- return Atari_POT(3);
- }
-
- mtype POKEY_POT4_GET(void)
- {
- return Atari_POT(4);
- }
-
- mtype POKEY_POT5_GET(void)
- {
- return Atari_POT(5);
- }
-
- mtype POKEY_POT6_GET(void)
- {
- return Atari_POT(6);
- }
-
- mtype POKEY_POT7_GET(void)
- {
- return Atari_POT(7);
- }
-
- mtype POKEY_RANDOM_GET(void)
- {
- static int rand_init = 0;
-
- if (!rand_init) {
- srand((int) time((time_t *) NULL));
- rand_init = 1;
- }
- return rand();
- }
-
- mtype POKEY_SERIN_GET(void)
- {
- return SIO_GetByte();
- }
-
- mtype POKEY_SKSTAT_GET(void)
- {
- #ifdef BASIC
- return 0xff;
- #else
- return Atari_Keyboard_State();
- #endif
- }
-
-
- /* IO Put routines */
-
- int POKEY_AUDF1_PUT(mtype val)
- {
- AUDF[CHAN1] = val;
- Update_Sound((AUDCTL & CH1_CH2)?((1<<CHAN2)|(1<<CHAN1)):(1<<CHAN1));
- return FALSE;
- }
-
- int POKEY_AUDC1_PUT(mtype val)
- {
- AUDC[CHAN1] = val;
- Update_Sound(1<<CHAN1);
- return FALSE;
- }
-
- int POKEY_AUDF2_PUT(mtype val)
- {
- AUDF[CHAN2] = val;
- Update_Sound(1<<CHAN2);
- return FALSE;
- }
-
- int POKEY_AUDC2_PUT(mtype val)
- {
- AUDC[CHAN2] = val;
- Update_Sound(1<<CHAN2);
- return FALSE;
- }
-
- int POKEY_AUDF3_PUT(mtype val)
- {
- AUDF[CHAN3] = val;
- Update_Sound((AUDCTL & CH3_CH4)?((1<<CHAN4)|(1<<CHAN3)):(1<<CHAN3));
- return FALSE;
- }
-
- int POKEY_AUDC3_PUT(mtype val)
- {
- AUDC[CHAN3] = val;
- Update_Sound(1<<CHAN3);
- return FALSE;
- }
-
- int POKEY_AUDF4_PUT(mtype val)
- {
- AUDF[CHAN4] = val;
- Update_Sound(1<<CHAN4);
- return FALSE;
- }
-
- int POKEY_AUDC4_PUT(mtype val)
- {
- AUDC[CHAN4] = val;
- Update_Sound(1<<CHAN4);
- return FALSE;
- }
-
- int POKEY_AUDCTL_PUT(mtype val)
- {
- AUDCTL = val;
-
- if (AUDCTL & POLY9)
- Poly17_size = POLY9_SIZE;
- else
- Poly17_size = POLY17_SIZE;
-
- /* determine the base multiplier for the 'div by n' calculations */
- if (AUDCTL & CLOCK_15)
- Base_mult = DIV_15;
- else
- Base_mult = DIV_64;
-
- Update_Sound((1<<CHAN1)|(1<<CHAN2)|(1<<CHAN3)|(1<<CHAN4));
- return FALSE;
- }
-
- int POKEY_IRQEN_PUT(mtype val)
- {
- IRQEN = val;
- IRQST |= ~val;
- return FALSE;
- }
-
- int POKEY_SEROUT_PUT(mtype val)
- {
- if ((SKSTAT & 0x70) == 0x20) {
- if ((AUDF[CHAN3] == 0x28) && (AUDF[CHAN4] == 0x00) && (AUDCTL & 0x28)==0x28)
- SIO_PutByte(val);
- }
- return FALSE;
- }
-
- int POKEY_STIMER_PUT(mtype val)
- {
- Div_n_cnt[CHAN1]=Div_n_irq[CHAN1]=0;
- Div_n_cnt[CHAN2]=Div_n_irq[CHAN2]=0;
- Div_n_cnt[CHAN3]=Div_n_irq[CHAN3]=0;
- Div_n_cnt[CHAN4]=Div_n_irq[CHAN4]=0;
- return FALSE;
- }
-
- int POKEY_SKSTAT_PUT(mtype val)
- {
- SKSTAT = val;
- return FALSE;
- }
-
-
- /*****************************************************************************/
- /* Module: Pokey_sound_init() */
- /* Purpose: to handle the power-up initialization functions */
- /* these functions should only be executed on a cold-restart */
- /* */
- /* Author: Ron Fries */
- /* Date: September 22, 1996 */
- /* */
- /* Inputs: freq17 - the value for the '1.79MHz' Pokey audio clock */
- /* playback_freq - the playback frequency in samples per second */
- /* */
- /* Outputs: Adjusts local globals - no return value */
- /* */
- /*****************************************************************************/
-
- void Init_Pokey(int *argc,char **argv,int base)
- {
- int chan;
- long n;
-
- /* fill the 17bit polynomial with random bits */
- for (n=0; n<POLY17_SIZE; n++)
- {
- bit17[n] = rand() & 0x01; /* fill poly 17 with random bits */
- }
-
- /* start all of the polynomial counters at zero */
- Poly_adjust = 0;
- P4 = 0;
- P5 = 0;
- P17 = 0;
-
- Samp_n_cnt[0] = 0; /* initialize all bits of the sample */
- Samp_n_cnt[1] = 0; /* 'divide by N' counter */
-
- Poly17_size = POLY17_SIZE;
- for (chan = CHAN1; chan <= CHAN4; chan++)
- {
- Outvol[chan] = 0;
- Outbit[chan] = 0;
- Div_n_cnt[chan] = 0;
- Div_n_irq[chan] = 0;
- Div_n_max[chan] = 0x7fffffffL;
- AUDC[chan] = 0;
- AUDF[chan] = 0;
- }
-
- AUDCTL = 0;
- IRQST = 0xff;
- IRQEN = 0x00;
- Base_mult = DIV_64;
- /*
- * Initialise Serial Port Interrupts
- */
-
- DELAYED_SERIN_IRQ = 0;
- DELAYED_SEROUT_IRQ = 0;
- DELAYED_XMTDONE_IRQ = 0;
-
- if (argc)
- Samp_n_max = 1; /* provide some meaningfull default if hard reset */
-
- SetHW(base+_AUDF1,0xff0f,&POKEY_POT0_GET,&POKEY_AUDF1_PUT);
- SetHW(base+_AUDC1,0xff0f,&POKEY_POT1_GET,&POKEY_AUDC1_PUT);
- SetHW(base+_AUDF2,0xff0f,&POKEY_POT2_GET,&POKEY_AUDF2_PUT);
- SetHW(base+_AUDC2,0xff0f,&POKEY_POT3_GET,&POKEY_AUDC2_PUT);
- SetHW(base+_AUDF3,0xff0f,&POKEY_POT4_GET,&POKEY_AUDF3_PUT);
- SetHW(base+_AUDC3,0xff0f,&POKEY_POT5_GET,&POKEY_AUDC3_PUT);
- SetHW(base+_AUDF4,0xff0f,&POKEY_POT6_GET,&POKEY_AUDF4_PUT);
- SetHW(base+_AUDC4,0xff0f,&POKEY_POT7_GET,&POKEY_AUDC4_PUT);
- SetHW(base+_AUDCTL,0xff0f,NULL,&POKEY_AUDCTL_PUT);
- SetHW(base+_STIMER,0xff0f,&POKEY_KBCODE_GET,&POKEY_STIMER_PUT);
- SetHW(base+_SKRES,0xff0f,&POKEY_RANDOM_GET,NULL);
- SetHW(base+_POTGO,0xff0f,NULL,NULL);
- SetHW(base+_SEROUT,0xff0f,&POKEY_SERIN_GET,&POKEY_SEROUT_PUT);
- SetHW(base+_IRQEN,0xff0f,&POKEY_IRQST_GET,&POKEY_IRQEN_PUT);
- SetHW(base+_SKCTLS,0xff0f,&POKEY_SKSTAT_GET,&POKEY_SKSTAT_PUT);
- }
-
- void Pokey_sound_init (ULONG freq17, UWORD playback_freq)
- {
- Samp_n_max = ((ULONG)freq17 << 8) / playback_freq;
- }
-
-
- /*****************************************************************************/
- /* Module: Update_Sound() */
- /* Purpose: To process the latest control values stored in the AUDF, AUDC, */
- /* and AUDCTL registers. It pre-calculates as much information as */
- /* possible for better performance. This routine has not been */
- /* optimized. */
- /* */
- /* Author: Ron Fries */
- /* Date: September 22, 1996 */
- /* */
- /* Inputs: addr - the address of the parameter to be changed */
- /* val - the new value to be placed in the specified address */
- /* */
- /* Outputs: Adjusts local globals - no return value */
- /* */
- /*****************************************************************************/
-
- void Update_Sound(int chan_mask)
- {
- ULONG new_val = 0;
- int chan;
-
- /************************************************************/
- /* As defined in the manual, the exact Div_n_cnt values are */
- /* different depending on the frequency and resolution: */
- /* 64 kHz or 15 kHz - AUDF + 1 */
- /* 1 MHz, 8-bit - AUDF + 4 */
- /* 1 MHz, 16-bit - AUDF[CHAN1]+256*AUDF[CHAN2] + 7 */
- /************************************************************/
-
- /* only reset the channels that have changed */
-
- if (chan_mask & (1 << CHAN1))
- {
- /* process channel 1 frequency */
- if (AUDCTL & CH1_179)
- new_val = AUDF[CHAN1] + 4;
- else
- new_val = (AUDF[CHAN1] + 1) * Base_mult;
-
- if (new_val != Div_n_max[CHAN1])
- {
- Div_n_max[CHAN1] = new_val;
- Div_n_cnt[CHAN1] = 0;
- Div_n_irq[CHAN1] = 0;
- }
- }
-
- if (chan_mask & (1 << CHAN2))
- {
- /* process channel 2 frequency */
- if (AUDCTL & CH1_CH2)
- if (AUDCTL & CH1_179)
- new_val = AUDF[CHAN2] * 256 + AUDF[CHAN1] + 7;
- else
- new_val = (AUDF[CHAN2] * 256 + AUDF[CHAN1] + 1) * Base_mult;
- else
- new_val = (AUDF[CHAN2] + 1) * Base_mult;
-
- if (new_val != Div_n_max[CHAN2])
- {
- Div_n_max[CHAN2] = new_val;
- Div_n_cnt[CHAN2] = 0;
- Div_n_irq[CHAN2] = 0;
- }
- }
-
- if (chan_mask & (1 << CHAN3))
- {
- /* process channel 3 frequency */
- if (AUDCTL & CH3_179)
- new_val = AUDF[CHAN3] + 4;
- else
- new_val= (AUDF[CHAN3] + 1) * Base_mult;
-
- if (new_val!= Div_n_max[CHAN3])
- {
- Div_n_max[CHAN3] = new_val;
- Div_n_cnt[CHAN3] = 0;
- Div_n_irq[CHAN3] = 0;
- }
- }
-
- if (chan_mask & (1 << CHAN4))
- {
- /* process channel 4 frequency */
- if (AUDCTL & CH3_CH4)
- if (AUDCTL & CH3_179)
- new_val = AUDF[CHAN4] * 256 + AUDF[CHAN3] + 7;
- else
- new_val = (AUDF[CHAN4] * 256 + AUDF[CHAN3] + 1) * Base_mult;
- else
- new_val = (AUDF[CHAN4] + 1) * Base_mult;
-
- if (new_val != Div_n_max[CHAN4])
- {
- Div_n_max[CHAN4] = new_val;
- Div_n_cnt[CHAN4] = 0;
- Div_n_irq[CHAN4] = 0;
- }
- }
-
- /* if channel is volume only, set current output */
- for (chan = CHAN1; chan <= CHAN4; chan++)
- {
- if (chan_mask & (1 << chan))
- {
- /* I've disabled any frequencies that exceed the sampling
- frequency. There isn't much point in processing frequencies
- that the hardware can't reproduce. I've also disabled
- processing if the volume is zero. */
-
- /* if the channel is volume only */
- /* or the channel is off (volume == 0) */
- /* or the channel freq is greater than the playback freq */
- if ((AUDC[chan] & VOL_ONLY) ||
- ((AUDC[chan] & VOLUME_MASK) == 0) ||
- (Div_n_max[chan] < (Samp_n_max >> 8)))
- {
- /* then set the channel to the selected volume */
- Outvol[chan] = AUDC[chan] & VOLUME_MASK;
- /* and set channel freq to max to reduce processing */
- /* Div_n_max[chan] = 0x7fffffffL;
- Removed. Conflicts with POKEY-IRQs */
- }
- }
- }
- }
-
-
- /*****************************************************************************/
- /* Module: Pokey_process_2() */
- /* Purpose: To fill the output buffer with the sound output based on the */
- /* pokey chip parameters. This routine has not been optimized. */
- /* Though it is not used by the program, I've left it for reference.*/
- /* */
- /* Author: Ron Fries */
- /* Date: September 22, 1996 */
- /* */
- /* Inputs: *buffer - pointer to the buffer where the audio output will */
- /* be placed */
- /* n - size of the playback buffer */
- /* */
- /* Outputs: the buffer will be filled with n bytes of audio - no return val */
- /* */
- /*****************************************************************************/
-
- void Pokey_process_2 (register unsigned char *buffer, register UWORD n)
- {
- #ifdef VOXWARE
- register ULONG *samp_cnt_w_ptr;
- register ULONG event_min;
- register UBYTE next_event;
- register UBYTE cur_val;
- register UBYTE chan;
-
- /* set a pointer to the whole portion of the samp_n_cnt */
- samp_cnt_w_ptr = (ULONG *)((UBYTE *)(&Samp_n_cnt[0])+1);
-
- /* loop until the buffer is filled */
- while (n)
- {
- /* Normally the routine would simply decrement the 'div by N' */
- /* counters and react when they reach zero. Since we normally */
- /* won't be processing except once every 80 or so counts, */
- /* I've optimized by finding the smallest count and then */
- /* 'accelerated' time by adjusting all pointers by that amount. */
-
- /* find next smallest event (either sample or chan 1-4) */
- next_event = SAMPLE;
- event_min = *samp_cnt_w_ptr;
-
- for (chan = CHAN1; chan <= CHAN4; chan++)
- {
- if (Div_n_cnt[chan] <= event_min)
- {
- event_min = Div_n_cnt[chan];
- next_event = chan;
- }
- }
-
-
- /* decrement all counters by the smallest count found */
- for (chan = CHAN1; chan <= CHAN4; chan++)
- {
- Div_n_cnt[chan] -= event_min;
- }
-
- *samp_cnt_w_ptr -= event_min;
-
- /* since the polynomials require a mod (%) function which is
- division, I don't adjust the polynomials on the SAMPLE events,
- only the CHAN events. I have to keep track of the change,
- though. */
- Poly_adjust += event_min;
-
- /* if the next event is a channel change */
- if (next_event != SAMPLE)
- {
- /* shift the polynomial counters */
- P4 = (P4 + Poly_adjust) % POLY4_SIZE;
- P5 = (P5 + Poly_adjust) % POLY5_SIZE;
- P17 = (P17 + Poly_adjust) % Poly17_size;
-
- /* reset the polynomial adjust counter to zero */
- Poly_adjust = 0;
-
- /* adjust channel counter */
- Div_n_cnt[next_event] += Div_n_max[next_event];
-
- /* From here, a good understanding of the hardware is required */
- /* to understand what is happening. I won't be able to provide */
- /* much description to explain it here. */
-
- /* if the output is pure or the output is poly5 and the poly5 bit */
- /* is set */
- if ((AUDC[next_event] & NOTPOLY5) || bit5[P5])
- {
- /* if the PURE bit is set */
- if (AUDC[next_event] & PURE)
- {
- /* then simply toggle the output */
- Outbit[next_event] = !Outbit[next_event];
- }
- /* otherwise if POLY4 is selected */
- else if (AUDC[next_event] & POLY4)
- {
- /* then use the poly4 bit */
- Outbit[next_event] = bit4[P4];
- }
- else
- {
- /* otherwise use the poly17 bit */
- Outbit[next_event] = bit17[P17];
- }
- }
-
- /* At this point I haven't emulated the filters. Though I don't
- expect it to be complicated, I don't believe this feature is
- used much anyway. I'll work on it later. */
- if ((next_event == CHAN1) || (next_event == CHAN3))
- {
- /* INSERT FILTER HERE */
- }
-
- /* if the current output bit is set */
- if (Outbit[next_event])
- {
- /* then set to the current volume */
- Outvol[next_event] = AUDC[next_event] & VOLUME_MASK;
- }
- else
- {
- /* set the volume to zero */
- Outvol[next_event] = 0;
- }
- }
- else /* otherwise we're processing a sample */
- {
- /* adjust the sample counter - note we're using the 24.8 integer
- which includes an 8 bit fraction for accuracy */
- *Samp_n_cnt += Samp_n_max;
-
- cur_val = 0;
-
- /* add the output values of all 4 channels */
- for (chan = CHAN1; chan <= CHAN4; chan++)
- {
- cur_val += Outvol[chan];
- }
-
- /* multiply the volume by 4 and add 8 to center around 128 */
- /* NOTE: this statement could be eliminated for efficiency, */
- /* though the volume would be lower. */
- cur_val = (cur_val << 2) + 8;
-
- /* add the current value to the output buffer */
- *buffer++ = cur_val;
-
- /* and indicate one less byte in the buffer */
- n--;
- }
- }
- #endif
- }
-
-
- /*****************************************************************************/
- /* Module: Pokey_process() */
- /* Purpose: To fill the output buffer with the sound output based on the */
- /* pokey chip parameters. This routine has not been optimized. */
- /* Though it is not used by the program, I've left it for reference.*/
- /* */
- /* Author: Ron Fries */
- /* Date: September 22, 1996 */
- /* */
- /* Inputs: *buffer - pointer to the buffer where the audio output will */
- /* be placed */
- /* n - size of the playback buffer */
- /* */
- /* Outputs: the buffer will be filled with n bytes of audio - no return val */
- /* */
- /*****************************************************************************/
-
- void Pokey_process (register unsigned char *buffer, register UWORD n)
- {
- #ifdef VOXWARE
-
- register ULONG *div_n_ptr;
- register ULONG *samp_cnt_w_ptr;
- register ULONG event_min;
- register UBYTE next_event;
- register UBYTE cur_val;
- register UBYTE *out_ptr;
- register UBYTE audc;
- register UBYTE toggle;
-
-
- /* set a pointer to the whole portion of the samp_n_cnt */
- samp_cnt_w_ptr = (ULONG *)((UBYTE *)(&Samp_n_cnt[0])+1);
-
- /* set a pointer for optimization */
- out_ptr = Outvol;
-
- /* The current output is pre-determined and then adjusted based on each */
- /* output change for increased performance (less over-all math). */
- /* add the output values of all 4 channels */
- cur_val = 2; /* start with a small offset */
- cur_val += *out_ptr++;
- cur_val += *out_ptr++;
- cur_val += *out_ptr++;
- cur_val += *out_ptr++;
-
- /* loop until the buffer is filled */
- while (n)
- {
- /* Normally the routine would simply decrement the 'div by N' */
- /* counters and react when they reach zero. Since we normally */
- /* won't be processing except once every 80 or so counts, */
- /* I've optimized by finding the smallest count and then */
- /* 'accelerated' time by adjusting all pointers by that amount. */
-
- /* find next smallest event (either sample or chan 1-4) */
- next_event = SAMPLE;
- event_min = *samp_cnt_w_ptr;
-
- /* Though I could have used a loop here, this is faster */
- div_n_ptr = Div_n_cnt;
- if (*div_n_ptr <= event_min)
- {
- event_min = *div_n_ptr;
- next_event = CHAN1;
- }
- div_n_ptr++;
- if (*div_n_ptr <= event_min)
- {
- event_min = *div_n_ptr;
- next_event = CHAN2;
- }
- div_n_ptr++;
- if (*div_n_ptr <= event_min)
- {
- event_min = *div_n_ptr;
- next_event = CHAN3;
- }
- div_n_ptr++;
- if (*div_n_ptr <= event_min)
- {
- event_min = *div_n_ptr;
- next_event = CHAN4;
- }
-
- /* decrement all counters by the smallest count found */
- /* again, no loop for efficiency */
- *div_n_ptr -= event_min;
- div_n_ptr--;
- *div_n_ptr -= event_min;
- div_n_ptr--;
- *div_n_ptr -= event_min;
- div_n_ptr--;
- *div_n_ptr -= event_min;
-
- *samp_cnt_w_ptr -= event_min;
-
- /* since the polynomials require a mod (%) function which is
- division, I don't adjust the polynomials on the SAMPLE events,
- only the CHAN events. I have to keep track of the change,
- though. */
- Poly_adjust += event_min;
-
- /* if the next event is a channel change */
- if (next_event != SAMPLE)
- {
- /* shift the polynomial counters */
- P4 = (P4 + Poly_adjust) % POLY4_SIZE;
- P5 = (P5 + Poly_adjust) % POLY5_SIZE;
- P17 = (P17 + Poly_adjust) % Poly17_size;
-
- /* reset the polynomial adjust counter to zero */
- Poly_adjust = 0;
-
- /* adjust channel counter */
- Div_n_cnt[next_event] += Div_n_max[next_event];
-
- /* get the current AUDC into a register (for optimization) */
- audc = AUDC[next_event];
-
- /* set a pointer to the current output (for opt...) */
- out_ptr = &Outvol[next_event];
-
- /* assume no changes to the output */
- toggle = FALSE;
-
- /* From here, a good understanding of the hardware is required */
- /* to understand what is happening. I won't be able to provide */
- /* much description to explain it here. */
-
- /* if the output is pure or the output is poly5 and the poly5 bit */
- /* is set */
- if ((audc & NOTPOLY5) || bit5[P5])
- {
- /* if the PURE bit is set */
- if (audc & PURE)
- {
- /* then simply toggle the output */
- toggle = TRUE;
- }
- /* otherwise if POLY4 is selected */
- else if (audc & POLY4)
- {
- /* then compare to the poly4 bit */
- toggle = (bit4[P4] == !(*out_ptr));
- }
- else
- {
- /* otherwise compare to the poly17 bit */
- toggle = (bit17[P17] == !(*out_ptr));
- }
- }
-
- /* At this point I haven't emulated the filters. Though I don't
- expect it to be complicated, I don't believe this feature is
- used much anyway. I'll work on it later. */
- if ((next_event == CHAN1) || (next_event == CHAN3))
- {
- /* INSERT FILTER HERE */
- }
-
- /* if the current output bit has changed */
- if (toggle)
- {
- if (*out_ptr)
- {
- /* remove this channel from the signal */
- cur_val -= *out_ptr;
-
- /* and turn the output off */
- *out_ptr = 0;
- }
- else
- {
- /* turn the output on */
- *out_ptr = audc & VOLUME_MASK;
-
- /* and add it to the output signal */
- cur_val += *out_ptr;
- }
- }
- }
- else /* otherwise we're processing a sample */
- {
- /* adjust the sample counter - note we're using the 24.8 integer
- which includes an 8 bit fraction for accuracy */
- *Samp_n_cnt += Samp_n_max;
-
- /* add the current value to the output buffer */
- *buffer++ = cur_val << 2;
-
- /* and indicate one less byte in the buffer */
- n--;
- }
- }
- #endif
- }
-
- /***************************************************************************
- ** Generate POKEY Timer IRQs if required **
- ** called on a per-scanline basis, not very precise, but good enough **
- ** for most applications **
- ***************************************************************************/
-
- void POKEY_Scanline(void)
- {
-
- if (DELAYED_SERIN_IRQ > 0) {
- if (--DELAYED_SERIN_IRQ == 0 ) {
- IRQST &= 0xdf;
- if (IRQEN & 0x20)
- GenerateIRQ();
- /* else printf("SerIn missing.\n"); */
- }
- }
-
- if (DELAYED_SEROUT_IRQ > 0) {
- if (--DELAYED_SEROUT_IRQ == 0 ) {
- IRQST &= 0xef;
- if (IRQEN & 0x10)
- GenerateIRQ();
- /* else printf("SerOut missing.\n"); */
- DELAYED_XMTDONE_IRQ += XMTDONE_INTERVAL;
- }
- }
-
- if (DELAYED_XMTDONE_IRQ > 0) {
- if (--DELAYED_XMTDONE_IRQ == 0) {
- IRQST &= 0xf7;
- if (IRQEN & 0x08)
- GenerateIRQ();
- /* else printf("XMTDone missing.\n"); */
- }
- }
-
- /* one scanline is 15Khz for an ordinary TV */
-
- if ((Div_n_irq[CHAN1] += DIV_15)>Div_n_max[CHAN1]) {
- Div_n_irq[CHAN1] = 0;
- if (IRQEN & 0x01) {
- IRQST &= 0xfe;
- GenerateIRQ();
- }
- }
-
- if ((Div_n_irq[CHAN2] += DIV_15)>Div_n_max[CHAN2]) {
- Div_n_irq[CHAN2] = 0;
- if (IRQEN & 0x02) {
- IRQST &= 0xfd;
- GenerateIRQ();
- }
- }
-
- if ((Div_n_irq[CHAN3] += DIV_15)>Div_n_max[CHAN3]) {
- Div_n_irq[CHAN3] = 0;
- }
-
- if ((Div_n_irq[CHAN4] += DIV_15)>Div_n_max[CHAN4]) {
- Div_n_irq[CHAN4] = 0;
- if (IRQEN & 0x04) {
- IRQST &= 0xfb;
- GenerateIRQ();
- }
- }
-
- }
-
- void SendKey(UBYTE key)
- {
- KBCODE = key;
- IRQST &= 0xbf;
- if (IRQEN & 0x40)
- GenerateIRQ();
- }
-
- void SendBRK(void)
- {
- IRQST &= 0x7f;
- if (IRQEN & 0x80)
- GenerateIRQ();
- }
-
-
-
-
-
-
-
-